package com.kinsin.kinsinutil

import java.io.*
import java.net.URLConnection
import java.nio.charset.Charset

/**
 * 文件操作工具类
 * @author kinsin
 *
 * // 创建文件
 * FileUtils.createFile("/sdcard/test/demo.txt")
 * <p>
 * // 写入内容
 * FileUtils.writeStringToFile("Hello World", "/sdcard/test/demo.txt")
 * <p>
 * // 复制文件
 * FileUtils.copy("/sdcard/test/demo.txt", "/sdcard/test/demo_copy.txt")
 * <p>
 * // 读取文件内容
 * val content = FileUtils.readFileToString("/sdcard/test/demo.txt")
 * <p>
 * // 获取目录大小
 * val size = FileUtils.getDirectorySize("/sdcard/test")
 * <p>
 * // 遍历文件
 * FileUtils.listFiles("/sdcard/test", true)?.forEach {
 *     println(it.absolutePath)
 * }
 * <p>
 * // 获取文件信息
 * val mimeType = FileUtils.getMimeType("/sdcard/test/demo.txt")
 * val extension = FileUtils.getFileExtension("/sdcard/test/demo.txt")
 */
object FileUtils {
    // region 基础文件操作
    
    /**
     * 创建文件（如果不存在）
     * @return 是否创建成功或已存在
     */
    fun createFile(filePath: String): Boolean = File(filePath).let {
        if (it.exists()) true else try {
            it.parentFile?.mkdirs()
            it.createNewFile()
        } catch (e: IOException) {
            false
        }
    }

    /**
     * 创建目录（如果不存在）
     * @return 是否创建成功或已存在
     */
    fun createDir(dirPath: String): Boolean = File(dirPath).let {
        if (it.exists()) true else it.mkdirs()
    }

    /**
     * 删除文件或目录（递归删除）
     */
    fun delete(path: String): Boolean = File(path).let {
        if (!it.exists()) return true
        if (it.isDirectory) {
            it.listFiles()?.forEach { child -> delete(child.absolutePath) }
        }
        return it.delete()
    }

    /**
     * 重命名文件/目录
     * @param newName 新名称（可包含路径）
     * @return 是否重命名成功
     */
    fun rename(path: String, newName: String): Boolean {
        return try {
            File(path).renameTo(File(newName))
        } catch (e: Exception) {
            false
        }
    }

    /**
     * 检查路径是否存在
     */
    fun exists(path: String): Boolean = File(path).exists()

    /**
     * 是否为文件
     */
    fun isFile(path: String): Boolean = File(path).isFile

    /**
     * 是否为目录
     */
    fun isDirectory(path: String): Boolean = File(path).isDirectory
    // endregion

    // region 文件读写操作

    /**
     * 写入字符串到文件
     * @param append 是否追加模式
     */
    fun writeStringToFile(
        content: String,
        filePath: String,
        charset: Charset = Charsets.UTF_8,
        append: Boolean = false
    ): Boolean = try {
        File(filePath).let { file ->
            file.parentFile?.mkdirs()
            // 使用支持追加模式的流写入
            FileOutputStream(file, append).writer(charset).use { writer ->
                writer.write(content)
            }
        }
        true
    } catch (e: Exception) {
        false
    }

    /**
     * 读取文件内容为字符串
     */
    fun readFileToString(filePath: String, charset: Charset = Charsets.UTF_8): String? = try {
        File(filePath).readText(charset)
    } catch (e: Exception) {
        null
    }

    /**
     * 写入字节数组到文件
     */
    fun writeBytesToFile(data: ByteArray, filePath: String, append: Boolean = false): Boolean = try {
        File(filePath).let { file ->
            file.parentFile?.mkdirs()
            if (append) {
                file.appendBytes(data)
            } else {
                file.writeBytes(data)
            }
        }
        true
    } catch (e: Exception) {
        false
    }

    /**
     * 读取文件为字节数组
     */
    fun readFileToBytes(filePath: String): ByteArray? = try {
        File(filePath).readBytes()
    } catch (e: Exception) {
        null
    }

    // endregion

    // region 文件操作增强
    
    /**
     * 复制文件或目录
     * @param overwrite 是否覆盖已存在文件
     */
    fun copy(srcPath: String, destPath: String, overwrite: Boolean = false): Boolean {
        val src = File(srcPath)
        val dest = File(destPath)
        
        if (!src.exists()) return false
        if (dest.exists()) {
            if (!overwrite) return false
            delete(destPath)
        }

        return if (src.isDirectory) {
            copyDirectory(src, dest)
        } else {
            copyFile(src, dest)
        }
    }

    /**
     * 移动文件/目录
     */
    fun move(srcPath: String, destPath: String, overwrite: Boolean = false): Boolean {
        return if (copy(srcPath, destPath, overwrite)) {
            delete(srcPath)
        } else {
            false
        }
    }

    // endregion

    // region 信息获取
    
    /**
     * 获取文件大小（字节）
     */
    fun getFileSize(filePath: String): Long = File(filePath).length()

    /**
     * 获取目录大小（递归计算）
     */
    fun getDirectorySize(dirPath: String): Long {
        val dir = File(dirPath)
        if (!dir.isDirectory) return 0
        return dir.walk().filter { it.isFile }.sumOf { it.length() }
    }

    /**
     * 获取文件扩展名
     */
    fun getFileExtension(filePath: String): String = File(filePath).name.substringAfterLast('.', "")

    /**
     * 获取MIME类型
     */
    fun getMimeType(filePath: String): String? = URLConnection.guessContentTypeFromName(File(filePath).name)

    /**
     * 列出目录下的文件
     * @param recursive 是否递归列出
     */
    fun listFiles(dirPath: String, recursive: Boolean = false): List<File>? = File(dirPath).let {
        if (!it.isDirectory) return null
        return if (recursive) it.walk().toList() else it.listFiles()?.toList()
    }

    // endregion

    // region 私有方法
    
    private fun copyFile(src: File, dest: File): Boolean {
        return try {
            src.inputStream().use { input ->
                dest.outputStream().use { output ->
                    input.copyTo(output)
                }
            }
            true
        } catch (e: Exception) {
            false
        }
    }

    private fun copyDirectory(src: File, dest: File): Boolean {
        if (!dest.mkdirs()) return false
        src.listFiles()?.forEach { file ->
            val newFile = File(dest, file.name)
            if (file.isDirectory) {
                if (!copyDirectory(file, newFile)) return false
            } else {
                if (!copyFile(file, newFile)) return false
            }
        }
        return true
    }

    // endregion

    // region 快捷方法
    
    fun getFile(path: String): File = File(path).also { it.parentFile?.mkdirs() }
    fun getFile(parent: String, child: String): File = File(parent, child).also { it.parentFile?.mkdirs() }

    // endregion
}